#pragma once

#include "evnet/http/client.hpp"

namespace evnet {
namespace http {

template<typename value_type, option_type O>
class basic_option
{
    const value_type value_;

public:

    explicit basic_option(const value_type& val) throw()
        : value_(val)
    {   }

    template<typename P>
    void apply(const P& p) const
    {
        set_option(p.handle(), O, value_);
    }
};

typedef basic_option<const char *, CURLOPT_URL> url;
typedef basic_option<const char *, CURLOPT_USERNAME> username;
typedef basic_option<const char *, CURLOPT_PASSWORD> password;
typedef basic_option<const char *, CURLOPT_USERPWD> userpwd;
typedef basic_option<const char *, CURLOPT_COOKIE> cookie;
typedef basic_option<const char *, CURLOPT_COOKIEFILE> cookie_file;
typedef basic_option<const char *, CURLOPT_PROXY> proxy;
typedef basic_option<const char *, CURLOPT_NOPROXY> noproxy;
typedef basic_option<const char *, CURLOPT_PROXYUSERNAME> proxy_username;
typedef basic_option<const char *, CURLOPT_PROXYPASSWORD> proxy_password;
typedef basic_option<const char *, CURLOPT_PROXYUSERPWD> proxy_userpwd;
typedef basic_option<const char *, CURLOPT_TLSAUTH_USERNAME> tlsauth_username;
typedef basic_option<const char *, CURLOPT_TLSAUTH_PASSWORD> tlsauth_password;
typedef basic_option<const char *, CURLOPT_REFERER> referer;
typedef basic_option<const char *, CURLOPT_USERAGENT> user_agent;
typedef basic_option<char *, CURLOPT_ERRORBUFFER> error_buffer;
typedef basic_option < const char *, CURLOPT_SOCKS5_GSSAPI_SERVICE >
    socks5_gssapi_service;
typedef basic_option<const char *, CURLOPT_NETRC_FILE> netrc_file;
typedef basic_option<const char *, CURLOPT_MAIL_FROM> mail_from;
//typedef basic_option<const char *, CURLOPT_MAIL_AUTH> mail_auth;
typedef basic_option < const char *, CURLOPT_FTP_ALTERNATIVE_TO_USER >
    ftp_alternative_to_user;
typedef basic_option<const char *, CURLOPT_FTP_ACCOUNT> ftp_account;

//typedef basic_multi_option<

class option
{
    const option_type opt_;
    const long value_;

public:

    explicit option(const option_type opt, const long val) throw()
        : opt_(opt)
        , value_(val)
    {   }

    template<typename P>
    void apply(const P& p) const
    {
        set_option(p.handle(), opt_, value_);
    }
};

class multi_option
{
    const moption_type opt_;
    const long value_;

public:

    explicit multi_option(const moption_type opt, const long val) throw()
        : opt_(opt)
        , value_(val)
    {   }

    template<typename P>
    void apply(const P& p) const
    {
        set_multi_option(p.handle(), opt_, value_);
    }
};

class escape
{
    char *text_{nullptr};

    escape(const escape&) = delete;
    escape& operator=(const escape&) = delete;
public:

    template<typename P>
    escape(const P& p, const std::string& text)
        : text_(curl_easy_escape(p.handle(), text.c_str(), text.size()))
    {
        if (!text_)
            throw std::runtime_error("curl_easy_escape");
    }

    escape(escape&& other)
    {
        std::swap(text_, other.text_);
    }

    escape& operator=(escape&& other)
    {
        if (this != &other)
            std::swap(text_, other.text_);

        return *this;
    }

    const char* c_str() const throw()
    {
        return text_;
    }

    std::string str() const
    {
        return std::string(text_);
    }

    ~escape()
    {
        if (text_)
            curl_free(text_);
    }
};

/*
class url
{
    std::string url_;

public:

    url(std::string text) throw()
        : url_(std::move(text))
    {   }

    template<std::size_t C>
    url(const char (&text)[C]) throw()
        : url_(text, C - 1)
    {   }

    template<typename P>
    void apply(const P& p) const
    {
        //escape esc(p, url_);
        set_option(p.handle(), CURLOPT_URL, url_.c_str());
    }
};
*/

// http://curl.haxx.se/libcurl/c/http-post.html
class post
{
    const char *buffer_{nullptr};
    bool copy_{true};

public:

    post(const char *buffer, bool copy = true) throw()
        : buffer_(buffer)
        , copy_(copy)
    {
        if (!buffer_)
            throw std::runtime_error("empty post data");
    }

    template<typename P>
    void apply(const P& p) const
    {
        set_option(p.handle(),
            (copy_) ? CURLOPT_COPYPOSTFIELDS : CURLOPT_POSTFIELDS,
            buffer_);
    }
};

} // namespace http
} // namespace evnet

